Tworzenie broni palnej
Na początku zdefiniujmy czym jest broń palna, jak działa i jakie są jej rodzaje. Od razu mówię, że w tym tutorialu omówię tworzenie wyłącznie jednego typu - pistoletu. Broń palna w świecie roblox to model składający się z: uchwytu, ciała i lufy. Uchwyt(handle) to część każdej broni w ROBLOX. Będzie to po prostu blok za który nasza postać będzie chwyciła broń. Ciało to cały model broni. Lufa zaś jest podłużnym walcem, z którego wylatywały będą kule. Działanie broni będzie niesamowicie proste; gdy gracz wciśnie LPM - wystrzelona zostanie kula w kierunku, w którym gracz celował. Co każdy strzał tracony zostaje jeden nabój z magazynku. Ilość naboi będą określały trzy zmienne - jedna będzie mówiła o całkowitym zapasie amunicji, druga o pojemności magazynku, a trzecia o ilości nabojów w tymże magazynku. Wyróżniamy głównie 4 typy broni palnej: Pistolety - mają słabą moc i duży rozrzut, każdy strzał wymaga oddzielnego wciśnięcia LPM; Strzelby - to samo, co pistolety z tą różnicą, że ich moc jest większa, a rozrzut mniejszy; Pistolety maszynowe - mają słabą moc i duży odrzut, ale wystrzały zachodzą nawet jeśli trzymany jest LPM; Karabiny maszynowe - tak jak pm z tym, że ich moc jest większa, a odrzut mniejszy od pm. W tym poradniku użyję pluginu Talon Hand Welding. Model Jak wyżej wspomniałem, model będzie składał się z trzech części. Najpierw stwórzmy ciało. Zróbmy je w dużej skali. Ważne jest to, że musisz być w trybie Edit: Teraz zaznacz wszystkie klocki ciała i połącz je w unię(naciśnij na zaznaczonych obiektach PPM i wybierz Union), po czym skaluj unię do właściwych rozmiarów: Teraz tylko musisz odznaczyć(o ile są zaznaczone) parametry unii Anchored i CanCollide. Nazwij powstałą unię(ja nazwę ją Body) W dokładnie taki sam sposób zrób "rączkę" i umieść ją tak, by była złączona z ciałem. Powstałą unię nazwij(TO WAŻNE) "Handle" i odznacz anchored i ZAZNACZ CanCollide: Teraz trzeba utworzyć trzecią część - lufę. Utwórz więc nowy klocek i dodaj do niego CylinderMesh. Umieśćmy lufę w broni, ustawmy CanCollide i Anchored na fałsz i nazwijmy lufę "Barrel": A teraz utwórz obiekt Tool i umieść w nim wszystkie trzy obiekty. Następnie zaznacz je(trzy obiekty) i za pomocą pluginu Talon Hand Welding wybierz z menu na górze Plugins>Handle Weld>Select some bricks with one named "Handle" and all the bricks will be welded to the Handle. A weld profile will be created and a script to re-weld the bricks for use in tools. To pozwala na to, by klocki trzymały się spójnie i nie odpadały. Ostatnie rzeczą którą teraz trzeba zrobić, jest ustawienie grip'u, czyli orientacji broni. Ustawić grip możesz za pomocą pól obiektu Tool. Ustawienia grip są trochę skomplikowane, więc ustaw go metodą prób i błędów w Solo Mode(a gdy już ustawisz broń, skopiuj ją i wklej w Edit Mode): U mnie wystarczyło zmienić GripForward na (-0.999, 0, 0.1). Oprócz klocków w naszej broni będziemy potrzebowali też kilka wartości opisujących jej stan i cechy. Stwórzmy dwie wartości typu int(obiekt IntValue) o nazwach "Ammo" i "StockAmmo" oraz dwie wartości typu number(obiekt NumberValue) o nazwach "Accuracy" i "Power" Ammo to liczba pocisków w magazynku, StockAmmo to liczba pocisków w zapasie, Accuracy to celność, a Power to siła pocisku. Skrypt Przejdźmy do najważniejszej części tutoriala - skryptu. Utwórzmy obiekt LocalScript w obiekcie Tool. Obiekty LocalScript w odróżnieniu od obiektów typu Script pozwalają na uzyskanie dostępu do obiektów nie przetrzymywanych na serwerze, lub mających powiązanie z graczem. Dzięki temu uzyskamy dostęp do obiektu gracza. Zacznijmy więc od inicjalizacji zmiennych na obiekty, które nam się w dalszej fazie przydadzą: gun = script.Parent; barrel = script.Parent:FindFirstChild("Barrel"); player = game.Players.LocalPlayer;--Dostep do gracza accuracy = script.Parent:FindFirstChild("Accuracy"); power = script.Parent:FindFirstChild("Power"); ammo = script.Parent:FindFirstChild("Ammo"); stock = script.Parent:FindFirstChild("StockAmmo"); reloading = false--czy bron jest w tym momencie przeladowywana A teraz napiszmy funkcję, która zostanie wywołana w razie przeładowania. Nazwijmy ją reload() Na początku zmieńmy wartość reloadink na prawdę i wykonajmy funkcję czekającą(powiedzmy 2.5 sekundy): function reload() reloading = true; wait(2.5); end Gdy magazynek będzie pełen, nie dojdzie do żadnej zmiany. W przeciwnym przypadku różnica między rozmiarem magazynku(w naszym przypadku 7), a ilością naboi zostanie dodana do magazynku i odjęta od zapasów, a w razie gdy liczba pocisków w zapasie będzie mniejsza od ww. różnicy, do magazynku zostanie dodana pozostała w zapasach liczba naboi. Może nie wygląda to do końca zrozumiale, ale mam nadzieję, że po dłuższym zapoznaniu się z przykładem wszystko zrozumiesz. Zapiszmy to dalej w funkcji reloading: --tu mozesz odtworzyc dzwiek przeladowanie(opcjonalne) if stock.Value >= 7 - ammo.Value then--jezeli w pociskow zapasie jest wiecej lub tyle samo ile brakuje do pelnego magazynka stock.Value = stock.Value - (7 - ammo.Value);--od zapasow odejmowana jest liczba potrzebnych do zapelnienia magazynku pociskow ammo.Value = 7;--magazynek jest uzupelniany do pelna else--jezeli liczba pociskow w zapasie nie wystarcza do calkowitego zapelnienia ammo.Value = stock.Value + ammo.Value;--dodana do magazynku zostaje reszta pociskow w zapasie stock.Value = 0;--oproznienie zapasow end I na końcu poinformujmy skrypt, że przeładowywanie zostało zakończone: reloading = false; Ufff... to mogło być ciężkie do przełknięcia. Teraz jednak zajmiemy się funkcją, która zostanie wywołana, gdy gracz będzie chciał oddać strzał. Funkcję tę nazwę shoot(). Funkcja ta przyjmie argument cfhit, który pozwoli na określenie miejsca i rotacji naciśnięcia LPM. Oczywiście aby doszło do tego strzału, liczba pocisków w magazynku musi być większa od zera, a broń nie może być w tym samym momencie przeładowywana. Po oddaniu strzału od liczby pocisków w magazynku zostanie odjęte 1(chyba wiadomo czemu): function shoot(cfhit) if not reloading and ammo.Value > 0 then--jezeli magazynek nie jest pusty, a bron nie jest przeladowywana, ammo.Value = ammo.Value - 1; end end Teraz zajmijmy się utworzeniem pocisku. Będzie to zwykły klocek z SpecialMesh pozwalającym na zmianę jego rozmiaru i kształtu na kulę: local bullet = Instance.new("Part", game.Workspace);--tworzymy pocisk local bulletShape = Instance.new("SpecialMesh", bullet);--i ksztalt pocisku bullet.BrickColor = BrickColor.new("Really black");--nadajemy mu kolor bullet.FormFactor = Enum.FormFactor.Custom;--i zmienamy budowe tak, bo mozna go bylo maksymalnie zmniejszyc bullet.Size = Vector3.new(0.2, 0.2, 0.2);--i zmniejszamy go bullet.Material = Enum.Material.Metal;--nadajemy mu metalowy material bulletShape.MeshType = Enum.MeshType.Sphere;--ksztalt kuli bulletShape.Scale = Vector3.new(0.7, 0.7, 0.7);--i jeszcze raz go zmniejszamy Po tym zajmijmy się jedną z najważniejszych rzeczy - pędem pocisku. Umieśćmy pocisk w koordynatach lufy. bullet.Position = barrel.Position; bullet.CFrame = CFrame.new(bullet.CFrame.p, cfhit.p); W drugiej linijce powyższego kodu znajduje się konstruktor CFrame, który działa tak, że na podstawie dwóch pozycji tworzy CFrame wraz z kątem między tymi dwoma pozycjami. Pocisk zostaje obrócony w stronę miejsca, gdzie dotyka kursor myszy. Zanim przejdziemy bezpośrednio do siły, zajmijmy się rozrzutem, czyli losowością kierunku wystrzelonych pocisków. Utwórzmy trzy losowe zmienne zmiennoprzecinkowe wahające się między liczbą określającą celność i jej przeciwnością: local dispX = math.random(-accuracy.Value, accuracy.Value); local dispY = math.random(-accuracy.Value, accuracy.Value); local dispZ = math.random(-accuracy.Value, accuracy.Value); Będą one odpowiadały za rozrzut względem danej osi. Teraz przejdźmy do samej siły. Na samym początku musimy ją utworzyć i "wrzucić" do naszego pocisku: local force = Instance.new("BodyForce", bullet); Po tym określmy kierunek tej siły. Jest on wyrażony w postaci Vector3, który można mnożyć przez zwykłe, jednowymiarowe wartości "skalując" go. Naszym kierunkiem siły będzie kierunkiem w który kula się "patrzy". Vector ten pomnożymy przez jakiś literał liczbowy(u mnie 20) i przez siłę broni: force.force = bullet.CFrame.lookVector * 20 * power.Value; Następnie zmodyfikujmy wektor siły w taki sposób, by dodać do niego wartości rozrzutu: force.force = Vector3.new(force.force.x + dispX, force.force.y + dispY, force.force.z + dispZ); Teraz nasz pocisk może lecieć, ale póki co nie wyrządzi nikomu najmniejszej krzywdy. Utwórzmy więc zdarzenie, które sprawdzi, czy kula dotknęła jakiegoś kolcka. Zdarzenie to powinieneś znać z poradnika dotyczącego tworzenia drzwi dla VIP'ów : bullet.Touched:connect(function(brick) end) Na początku sprawdźmy, czy klocek jest postacią. W takim wypadku jego rodzic zawiera obiekt Humanoid: local human = brick.Parent:FindFirstChild("Humanoid"); if human ~= nil then end Teraz w zależności od miejsca trafienia odejmujemy odpowiednią ilość HP pomnożoną przez siłę broni. Miejsce trafienia to jest zawarte w nazwie trafionego klocka. Oto przykład: if brick.Name "Torso" then human.Health = human.Health - 35 * power.Value; elseif brick.Name "Head" then human.Health = human.Health - 60 * power.Value; elseif brick.Name "Left Arm" or brick.Name "Right Arm" then human.Health = human.Health - 20 * power.Value; elseif brick.Name "Left Leg" or brick.Name "Right Leg" then human.Health = human.Health - 10 * power.Value; end Teraz wystarczy wywołać napisane funkcje dla odpowiednich zdarzeń: Sprawdźmy najpierw, czy postać nosi naszą spluwę poprzez event Equipped: Event ten zwraca obiekt myszy: gun.Equipped:connect(function(mouse) end) Następnie nasz skrypt powinien sprawdzić, czy gracz wcisnął klawisz, a następnie sprawdzić, czy ten klawisz to r''. Jeżeli tak, to wykonana zostanie funkcja ''reload(): mouse.KeyDown:connect(function(key) if key "r" then reload(); end end) Sprawdźmy jeszcze, czy gracz wcisnął LPM. Jeżeli tak, to wykonana zostanie funkcja shoot(), do której argumentu "wsadzimy" parametr Hit obiektu myszy, czyli CFrame mówiący o pozycji myszy na obszarze 3D: mouse.Button1Down:connect(function() shoot(mouse.Hit); end) Cały skrypt gun = script.Parent; barrel = script.Parent:FindFirstChild("Barrel"); player = game.Players.LocalPlayer;--Dostep do gracza accuracy = script.Parent:FindFirstChild("Accuracy"); power = script.Parent:FindFirstChild("Power"); ammo = script.Parent:FindFirstChild("Ammo"); stock = script.Parent:FindFirstChild("StockAmmo"); reloading = false--czy bron jest w tym momencie przeladowywana function reload() reloading if stock.Value >= 7 - ammo.Value then--jezeli w pociskow zapasie jest wiecej lub tyle samo ile brakuje do pelnego magazynka stock.Value = stock.Value - (7 - ammo.Value);--od zapasow odejmowana jest liczba potrzebnych do zapelnienia magazynku pociskow ammo.Value = 7;--magazynek jest uzupelniany do pelna else--jezeli liczba pociskow w zapasie nie wystarcza do calkowitego zapelnienia ammo.Value = stock.Value + ammo.Value;--dodana do magazynku zostaje reszta pociskow w zapasie stock.Value = 0;--oproznienie zapasow reloading = false; end = true; wait(2.5); end function shoot(cfhit) if not reloading and ammo.Value > 0 then--jezeli magazynek nie jest pusty, a bron nie jest przeladowywana, ammo.Value = ammo.Value - 1; local bullet = Instance.new("Part", game.Workspace);--tworzymy pocisk local bulletShape = Instance.new("SpecialMesh", bullet);--i ksztalt pocisku bullet.BrickColor = BrickColor.new("Really black");--nadajemy mu kolor bullet.FormFactor = Enum.FormFactor.Custom;--i zmienamy budowe tak, bo mozna go bylo maksymalnie zmniejszyc bullet.Size = Vector3.new(0.2, 0.2, 0.2);--i zmniejszamy go bullet.Material = Enum.Material.Metal;--nadajemy mu metalowy material bulletShape.MeshType = Enum.MeshType.Sphere;--ksztalt kuli bulletShape.Scale = Vector3.new(0.7, 0.7, 0.7);--i jeszcze raz go zmniejszamy bullet.Position = barrel.Position; bullet.CFrame = CFrame.new(bullet.CFrame.p, cfhit.p); local dispX = math.random(-accuracy.Value, accuracy.Value); local dispY = math.random(-accuracy.Value, accuracy.Value); local dispZ = math.random(-accuracy.Value, accuracy.Value); local force = Instance.new("BodyForce", bullet); force.force = bullet.CFrame.lookVector * 20 * power.Value; force.force = Vector3.new(force.force.x + dispX, force.force.y + dispY, force.force.z + dispZ); bullet.Touched:connect(function(brick) end) if brick.Name "Torso" then human.Health = human.Health - 35 * power.Value; elseif brick.Name "Head" then human.Health = human.Health - 60 * power.Value; elseif brick.Name "Left Arm" or brick.Name "Right Arm" then human.Health = human.Health - 20 * power.Value; elseif brick.Name "Left Leg" or brick.Name "Right Leg" then human.Health = human.Health - 10 * power.Value; end gun.Equipped:connect(function(mouse) end) mouse.KeyDown:connect(function(key) if key "r" then reload(); end end) mouse.Button1Down:connect(function() shoot(mouse.Hit); end) end end Kategoria:Poradniki